package com.yhally.seata.order.tcc;

import cn.hutool.core.bean.BeanUtil;
import cn.wangyu.tcc.record.domain.TransactionalRecord;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.yhally.seata.common.constants.NumberConstant;
import com.yhally.seata.common.utils.ResultHolder;
import com.yhally.seata.order.dao.OrderDao;
import com.yhally.seata.order.domain.Order;
import com.yhally.seata.order.feign.TransactionRecordFeign;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;

/**
 * @author wangyu
 * @version 1.0
 * @date 2022/5/8 16:54
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class OrderTccActionImpl implements OrderTccAction {

    private final OrderDao orderDao;
    private final TransactionRecordFeign transactionRecordFeign;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean creatOrder(BusinessActionContext businessActionContext, Order order) {
        //判断cancel阶段是否已经执行过--事务日志 为了防止悬挂异常，从事务日志表中获取全局事务ID的状态，如果是cancel状态则不执行
        R<TransactionalRecord> recordR = transactionRecordFeign.getByXid(businessActionContext.getXid());
        TransactionalRecord transactionalRecord = recordR.getData();
        if (Objects.equals(recordR.getCode(), NumberConstant.TWO_HUNDRED)) {
            throw new RuntimeException("事务日志查询失败！");
        }
        if (transactionalRecord != null && Objects.equals(transactionalRecord.getStatus(), NumberConstant.THREE)) {
            throw new RuntimeException("已经进入了TCC第二阶段的cancel阶段，不允许try阶段！");
        }

        // 冻结库存
        // 创建订单并设置订单状态为 创建中
        orderDao.insert(order);

        // 保证幂等
        ResultHolder.setResult(getClass(),businessActionContext.getXid(),String.valueOf(System.currentTimeMillis()));
        log.info("创建订单：tcc一阶段try成功");
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean commit(BusinessActionContext businessActionContext) {
        /*
            提交订单，将订单状态修改为已完成，从businessActionContext获取一阶段传递的参数

            注意此处如果业务代码执行错误，会一直回滚，但是扣减库存会成功，导致整个下单流程出现问题，因为commit阶段貌似是异步操作；
            所以在整个流程中应该保证commit阶段不会出现错误。一旦出现错误貌似只能通过人工解决代码错误，再解决订单数据一致问题
         */
        log.info("创建订单：tcc二阶段commit开始,[{}]", System.currentTimeMillis());

        // 校验幂等，防止多次提交
        if (Objects.isNull(ResultHolder.getResult(getClass(),businessActionContext.getXid()))) {
            return true;
        }
        JSONObject jsonObject = (JSONObject) businessActionContext.getActionContext("order");
        Order order = new Order();
        BeanUtil.copyProperties(jsonObject, order);

        order.setStatus(NumberConstant.ONE);
        orderDao.update(order, new QueryWrapper<Order>().eq("no", order.getNo()));

        log.info("创建订单：tcc二阶段commit成功");
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rollback(BusinessActionContext businessActionContext) {
        // 防止悬挂
        TransactionalRecord transactionalRecord = TransactionalRecord.builder()
                .xid(businessActionContext.getXid())
                .status(2)
                .build();
        R<TransactionalRecord> recordR = transactionRecordFeign.add(transactionalRecord);
        if (!Objects.equals(recordR.getCode(),NumberConstant.TWO_HUNDRED)) {
            return false;
        }

        // 校验幂等性 空回滚，如果为空证明已经删除此条事务记录，不比再执行回滚
        if (Objects.isNull(ResultHolder.getResult(getClass(),businessActionContext.getXid()))) {
            return true;
        }
        // 回滚 将订单删除
        JSONObject jsonObject = (JSONObject) businessActionContext.getActionContext("order");
        Order order = new Order();
        BeanUtils.copyProperties(jsonObject, order);
        orderDao.delete(new QueryWrapper<Order>().eq("no", order.getNo()));

        // 回滚成功，移除
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        log.info("创建订单：tcc二阶段回滚成功");
        return true;
    }
}
